package com.xinqi.modules.prepare.question.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xinqi.common.security.utils.UserHelp;
import com.xinqi.common.base.dto.QuestionBaseDTO;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.utils.lambda.Lambdas;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.modules.prepare.common.dto.data.PrepareQuestionBankData;
import com.xinqi.modules.prepare.common.enums.PrepareLimitEnum;
import com.xinqi.modules.prepare.common.service.PrepareAuthService;
import com.xinqi.modules.prepare.paper.dto.req.QuestionTypeNumDTO;
import com.xinqi.modules.prepare.prepare.enums.PrepareMemberPermissionEnum;
import com.xinqi.modules.prepare.prepare.enums.PrepareOperateLogEnum;
import com.xinqi.modules.prepare.prepare.manager.PrepareOperateLogManager;
import com.xinqi.modules.prepare.question.convert.PrepareQuestionBankConvert;
import com.xinqi.modules.prepare.question.domain.PrepareQuestionBankEntity;
import com.xinqi.modules.prepare.question.dto.QuestionScoreDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankCopyDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankCreateDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankDeleteDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankEditScoreDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankQueryDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankSortDTO;
import com.xinqi.modules.prepare.question.dto.req.PrepareQuestionBankUpdateDTO;
import com.xinqi.modules.prepare.question.dto.rsp.PrepareQuestionBankResultDTO;
import com.xinqi.modules.prepare.question.dto.rsp.PrepareQuestionStatsDataDTO;
import com.xinqi.modules.prepare.question.manager.PrepareQuestionBankManager;
import com.xinqi.modules.prepare.question.service.PrepareQuestionBankService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.xinqi.common.base.error.CommonErrors.BAD_REQUEST;

/**
 * 备课区测评卷试题表服务层实现
 *
 * @author: system
 * @date: 2023/03/19
 */
@Validated
@Service("prepareQuestionBankService")
@RequiredArgsConstructor
public class PrepareQuestionBankServiceImpl implements PrepareQuestionBankService {
    private final PrepareQuestionBankConvert prepareQuestionBankConvert;
    private final PrepareQuestionBankManager prepareQuestionBankManager;
    private final PrepareOperateLogManager prepareOperateLogManager;
    private final PrepareAuthService prepareAuthService;

    /**
     * 分页查询 备课区测评卷试题表数据
     */
    @Override
    @DS("slaver")
    public PageInfo<PrepareQuestionBankResultDTO> page(Param pageable, PrepareQuestionBankQueryDTO query) {
        IPage<PrepareQuestionBankEntity> page = prepareQuestionBankManager.page(Pages.page(pageable), wrapper(query));
        return Pages.convert(page, prepareQuestionBankConvert::convert);
    }

    /**
     * 查询 备课区测评卷试题表数据
     */
    @Override
    public List<PrepareQuestionBankResultDTO> find(PrepareQuestionBankQueryDTO query) {
        return prepareQuestionBankManager.list(wrapper(query)).stream().map(prepareQuestionBankConvert::convert).collect(Collectors.toList());
    }

    private LambdaQueryWrapper<PrepareQuestionBankEntity> wrapper(PrepareQuestionBankQueryDTO query) {
        LambdaQueryWrapper<PrepareQuestionBankEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(PrepareQuestionBankEntity::getId, v));
        Queries.accept(query.getName(), v -> wrapper.like(PrepareQuestionBankEntity::getQuestion, v));
        Queries.accept(query.getPrepareId(), v -> wrapper.eq(PrepareQuestionBankEntity::getPrepareId, v));
        Queries.accept(query.getType(), v -> wrapper.eq(PrepareQuestionBankEntity::getQuestionType, v.getCode()));
        Queries.accept(query.getQuestionTypeList(), w -> wrapper.in(PrepareQuestionBankEntity::getQuestionType, w));
        return wrapper;
    }

    /**
     * 根据id查询 备课区测评卷试题表数据
     */
    @Override
    public PrepareQuestionBankResultDTO findById(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(prepareQuestionBankManager.findById(id)).map(prepareQuestionBankConvert::convert).orElse(null);
    }

    @Override
    public PrepareQuestionBankData find(Long id) {
        return prepareQuestionBankConvert.convert(findById(id));
    }

    /**
     * 新增 备课区测评卷试题表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(@Valid PrepareQuestionBankCreateDTO dto) {
        PrepareQuestionBankEntity entity = prepareQuestionBankConvert.create(dto);
        entity.setUserId(UserHelp.userId());
        prepareQuestionBankManager.save(entity);
        // 新增题目限制
        long questionTotal = prepareQuestionBankManager.countByPrepareId(dto.getPrepareId());
        PrepareLimitEnum.备课区题目限制.check(questionTotal + 1);
        // 添加日志
        prepareOperateLogManager.addQuestionOperateLog(entity, PrepareOperateLogEnum.QUESTION_ADD);
        return entity.getId();
    }

    @Override
    public Long insert(PrepareQuestionBankData data) {
        PrepareQuestionBankCreateDTO dto = prepareQuestionBankConvert.create(data);
        return insert(dto);
    }

    /**
     * 根据id修改 备课区测评卷试题表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateById(@Valid PrepareQuestionBankUpdateDTO dto) {
        PrepareQuestionBankEntity old = Optional.ofNullable(prepareQuestionBankManager.getById(dto.getId())).orElseThrow(() -> BAD_REQUEST.asException("数据不存在"));
        prepareOperateLogManager.addQuestionOperateLog(old, PrepareOperateLogEnum.QUESTION_EDIT);
        PrepareQuestionBankEntity entity = prepareQuestionBankConvert.update(dto);
        return prepareQuestionBankManager.updateById(entity);
    }

    /**
     * 根据id删除 备课区测评卷试题表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        checkDelete(id);
        return prepareQuestionBankManager.removeById(id);
    }

    /**
     * 根据id删除 备课区测评卷试题表数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(PrepareQuestionBankDeleteDTO dto) {
        return deleteById(dto.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteBatch(Long prepareId, List<Long> ids) {
        Lambdas.each(ids, this::checkDelete);
        return prepareQuestionBankManager.removeByIds(ids);
    }

    private void checkDelete(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        PrepareQuestionBankEntity entity = Optional.ofNullable(prepareQuestionBankManager.getById(id)).orElseThrow(() -> BAD_REQUEST.asException("数据不存在"));
        prepareAuthService.check(entity.getPrepareId(), PrepareMemberPermissionEnum.删除);
        prepareOperateLogManager.addQuestionOperateLog(entity, PrepareOperateLogEnum.QUESTION_DEL);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editQuestionScore(@Valid PrepareQuestionBankEditScoreDTO dto) {
        prepareAuthService.check(dto.getPrepareId(), PrepareMemberPermissionEnum.编辑);
        List<QuestionScoreDTO> scores = dto.getScores();
        BAD_REQUEST.check(CollectionUtils.isNotEmpty(scores), "分数列表不为空");
        return prepareQuestionBankManager.editQuestionScore(dto.getPrepareId(), dto.getScores());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean sortQuestionBank(@Valid PrepareQuestionBankSortDTO dto) {
        prepareAuthService.check(dto.getPrepareId(), PrepareMemberPermissionEnum.编辑);
        return prepareQuestionBankManager.sortQuestionBank(dto.getPrepareId(), dto.getSorts());
    }

    @Override
    public PrepareQuestionStatsDataDTO questionStats(Long prepareId) {
        return prepareQuestionBankManager.questionStats(prepareId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertBatch(Long prepareId, List<PrepareQuestionBankCreateDTO> list) {
        prepareAuthService.check(prepareId, PrepareMemberPermissionEnum.新建);
        BAD_REQUEST.check(CollectionUtils.isNotEmpty(list), "题目列表不为空");
        for (PrepareQuestionBankCreateDTO dto : list) {
            insert(dto);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean copy(@Valid PrepareQuestionBankCopyDTO dto) {
        prepareAuthService.check(dto.getPrepareId(), PrepareMemberPermissionEnum.复制到);
        prepareAuthService.check(dto.getTargetPrepareId(), PrepareMemberPermissionEnum.新建);
        List<Long> questionIds = dto.getQuestionIds();
        BAD_REQUEST.check(questionIds.size() <= 150, "复制试题数量，不能超过200题");
        List<PrepareQuestionBankEntity> list = prepareQuestionBankManager.listByIds(questionIds);
        if (CollectionUtils.isEmpty(list)) {
            throw BAD_REQUEST.asException("未找到题目");
        }
        long questionTotal = prepareQuestionBankManager.countByPrepareId(dto.getTargetPrepareId());
        PrepareLimitEnum.备课区题目限制.check(questionTotal + list.size());
        // 初始化数据
        list.forEach(question -> {
            question.setId(null);
            question.setPrepareId(dto.getTargetPrepareId());
            question.setUserId(UserHelp.userId());
            question.setCreateBy(null);
            question.setCreateTime(null);
            question.setUpdateBy(null);
            question.setUpdateTime(null);
        });
        return prepareQuestionBankManager.saveBatch(list);
    }

    @Override
    public List<QuestionBaseDTO> random(Long prepareId, @Valid List<QuestionTypeNumDTO> dto) {
        return prepareQuestionBankManager.findByRandom(prepareId, dto);
    }


    /**
     * 查询 备课区测评卷试题表数据 根据IDS
     */
    @Override
    public List<PrepareQuestionBankResultDTO> findIds(List<Long> ids){
        LambdaQueryWrapper<PrepareQuestionBankEntity> wrapper = Queries.lambda();
        wrapper.in(PrepareQuestionBankEntity::getId, ids);
        return prepareQuestionBankManager.list(wrapper).stream().map(prepareQuestionBankConvert::convert).collect(Collectors.toList());
    }

}
